from machine import sleep, SoftI2C, Pin, Timer, RTC, I2C
from utime import ticks_diff, ticks_us
from max30102 import MAX30102, MAX30105_PULSE_AMP_MEDIUM
from hrcalc import calc_hr_and_spo2
import network
import ntptime
import time
import mpu6050
from umqttsimple import MQTTClient

# ------------ 添加新代码 -----------
import ssd1306

button_name = 0
BEATS = 0  # 存储心率
FINGER_FLAG = False  # 默认表示未检测到手指
SPO2 = 0  # 存储血氧
TEMPERATURE = 0  # 存储温度
HREAT_LIST = []  # 存储心率的最新30次数据
SPO2_LIST = []  # 存储血氧的最新10次数据
TEMP_LIST = []  # 存储温度的最新10次数据
# ------------ 添加新代码 -----------
OLED = None


def button_setup():
    global button_de
    button_de = Pin(21, Pin.IN, Pin.PULL_UP)

    button_de.irq(trigger=Pin.IRQ_FALLING, handler=button_detect)


def button_detect(chn):
    global button_name
    button_name += 1
    if button_name == 1:
        print(1)
    elif button_name == 2:
        button_name = 0
        print(0)


def makerobo_setup(pin):
    global makerobo_BuzzerPin
    global buzzer
    makerobo_BuzzerPin = pin
    buzzer = Pin(makerobo_BuzzerPin, Pin.OUT)
    buzzer.value(1)


def turn_on():
    buzzer.value(0)


def turn_off():
    buzzer.value(1)


def time_onandoff():
    turn_on()
    time.sleep(1)
    turn_off()
    time.sleep(1)


def sub_cb(topic, msg):  # 回调函数，收到服务器消息后会调用这个函数
    print(topic, msg)


class Clock:
    msg = ""

    def __init__(self):
        self.connect_wifi('Xiaomi_FA69', 'zhonghao110')
        self.ntp()
        self.rtc = RTC()

        # 调用这个方法来联网设置时间
        self.second_light = False

    def connect_wifi(self, wifi, password):
        wlan = network.WLAN(network.STA_IF)
        wlan.active(True)
        if not wlan.isconnected():
            print('connecting to network...')
            wlan.connect(wifi, password)
            i = 1
            while not wlan.isconnected() and i < 10:
                print("正在链接...{}".format(i))
                i += 1
                time.sleep(1)
        print('network config:', wlan.ifconfig())

    def ntp(self):
        self.connect_wifi('Xiaomi_FA69', 'zhonghao110')
        # 东八区 UTC+8偏移时间（秒）
        ntptime.host = "ntp1.aliyun.com"
        # ntptime.NTP_DELTA = 3155644800
        time.sleep(1)
        # self.connect_wifi()
        ntptime.settime()

    def get_message(self):
        global msg
        return msg

    def get_time(self):
        global msg
        date = self.rtc.datetime()
        if (date[0] == 2000):
            msg = "Networking error"
        else:
            msg = "Online"

        # 获取真正的时间

        return date


def create_oled0():
    global OLED, HREAT_LIST, SPO2_LIST, TEMP_LIST, OLED, BEATS, SPO2, TEMPERATURE
    # set a specific date and time
    # 创建I2C对象(驱动屏幕)
    i2c_oled = SoftI2C(scl=Pin(25), sda=Pin(26))

    # 宽度高度
    oled_width = 128
    oled_height = 64
    # 创建oled屏幕对象
    OLED = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c_oled)

    OLED.fill(0)

    OLED.text('Wifi connecting', 0, 30)  # 初始化屏幕显示的内容
    OLED.show()


#def oledinterface1():



#def oledinterface2():


def display_info(t):  # 代码中循环使用的内容 1秒循环一次
    # ------------ 修改代码 -----------

    accel_dict = accel.get_values()
    X = accel_dict.get('GyX')
    Y = accel_dict.get('GyY')
    Z = accel_dict.get('GyZ')
    # print("X:",X)
    # print("Y:",Y)
    # print("Z:",Z)
    server.check_msg()
    # 如果没有检测到手指，那么就不显示
    if FINGER_FLAG is False and button_name == 1:
        HREAT_LIST.clear()
        SPO2_LIST.clear()
        TEMP_LIST.clear()
        # ------------ 添加新代码 -----------
        BEATS = SPO2 = TEMPERATURE = 0
        OLED.fill(0)
        show_time()
        OLED.text('Put your finger', 0, 30)
        OLED.text('for testing', 0, 40)
        OLED.text(clock.get_message(), 0, 50)

        # OLED屏幕显示 放手指
        OLED.show()
        return

    # ------------ 添加新代码 -----------
    if len(HREAT_LIST) < 30:
        bmp_str = 'BPM :%3d' % int(BEATS)
        server.publish(b"MQTT", str(BEATS))
    else:
        bmp_str = 'BPM :%3d' % int(sum(HREAT_LIST[20:]) / len(HREAT_LIST[20:]))

    if len(SPO2_LIST) < 10:
        spo2_str = 'SPO2:%3d' % int(SPO2)
        server.publish(b"spo2", str(SPO2))
    else:
        spo2_str = 'SPO2:%3d' % int(sum(SPO2_LIST[5:]) / len(SPO2_LIST[5:]))

    if len(TEMP_LIST) < 10:
        temp_str = 'TEMP:%3d' % int(TEMPERATURE)
        server.publish(b"temp", str(TEMPERATURE))
    else:
        temp_str = 'TEMP:%3d' % int(sum(TEMP_LIST[5:]) / len(TEMP_LIST[5:]))

    if len(HREAT_LIST) < 30:
        HREAT_LIST.append(BEATS)
        HREAT_LIST = HREAT_LIST[-30:]  # 保证列表最多30个元素
        bmp_str += " <-- %d" % (30 - len(HREAT_LIST))
    elif len(SPO2_LIST) < 10:
        SPO2_LIST.append(SPO2)
        SPO2_LIST = SPO2_LIST[-10:]  # 保证列表最多10个元素
        spo2_str += " <-- %d" % (10 - len(SPO2_LIST))
    elif len(TEMP_LIST) < 10:
        TEMP_LIST.append(TEMPERATURE)
        TEMP_LIST = TEMP_LIST[-10:]  # 保证列表最多10个元素
        temp_str += " <-- %d" % (10 - len(TEMP_LIST))
    OLED.fill(0)
    OLED.text(bmp_str, 0, 30)
    OLED.text(spo2_str, 0, 40)
    OLED.text(temp_str, 0, 50)
    OLED.show()


def show_time():
    date = clock.get_time()
    date_hour = date[4] + 8
    if (date_hour) >= 24:
        date_hour = date_hour - 24

    if (date_hour == 20 and date[5] == 03):
        turn_on()
    else:
        turn_off()

    # 清空
    year = str(date[0])
    month = str(date[1])
    day = str(date[2])
    hour = str(date_hour)
    minutes = str(date[5])
    s = str(date[6])
    if date[3] == 0:
        dayplus = "Monday"
    elif date[3] == 1:
        dayplus = "Tuesday"
    elif date[3] == 2:
        dayplus = "Wednesday"
    elif date[3] == 3:
        dayplus = "Thursday"
    elif date[3] == 4:
        dayplus = "Friday"
    elif date[3] == 5:
        dayplus = "Saturday"
    elif date[3] == 6:
        dayplus = "Sunday"
    if date_hour < 10:
        hour = "0" + str(date_hour)
    if date[5] < 10:
        minutes = "0" + str(date[5])
    if date[6] < 10:
        s = "0" + str(date[6])

    # print(hour)
    OLED.text(year + "/" + month + "/" + day, 0, 0)
    OLED.text(dayplus, 0, 10)
    OLED.text(hour + ":" + minutes + ":" + s, 0, 20)

    # 时（2位）

    # 显示


def dataGetting():
    global BEATS, FINGER_FLAG, SPO2, TEMPERATURE  # 如果需要对全局变量修改，则需要global声明

    # 创建I2C对象(检测MAX30102)
    i2c = SoftI2C(sda=Pin(16), scl=Pin(17), freq=400000)  # Fast: 400kHz, slow: 100kHz

    # 创建传感器对象
    sensor = MAX30102(i2c=i2c)

    # 检测是否有传感器
    if sensor.i2c_address not in i2c.scan():
        print("没有找到传感器")
        return
    elif not (sensor.check_part_id()):
        # 检查传感器是否兼容
        print("检测到的I2C设备不是MAX30102或者MAX30105")
        return
    else:
        print("传感器已识别到")

    # 配置
    sensor.setup_sensor()
    sensor.set_sample_rate(400)
    sensor.set_fifo_average(8)
    sensor.set_active_leds_amplitude(MAX30105_PULSE_AMP_MEDIUM)

    t_start = ticks_us()  # Starting time of the acquisition

    MAX_HISTORY = 32
    history = []
    beats_history = []
    beat = False
    red_list = []
    ir_list = []

    while True:

        sensor.check()
        if sensor.available():
            # FIFO 先进先出，从队列中取数据。都是整形int
            red_reading = sensor.pop_red_from_storage()
            ir_reading = sensor.pop_ir_from_storage()

            if red_reading < 1000:
                history.clear()
                beats_history.clear()
                FINGER_FLAG = False  # 表示没有放手指
                continue
            else:
                FINGER_FLAG = True  # 表示手指已放

            # ------------ 修改代码 -----------
            if len(HREAT_LIST) < 30:
                # 计算心率
                history.append(red_reading)

                # 为了防止列表过大，这里取列表的后32个元素
                history = history[-MAX_HISTORY:]

                # 提取必要数据
                minima, maxima = min(history), max(history)
                threshold_on = (minima + maxima * 3) // 4  # 3/4
                threshold_off = (minima + maxima) // 2  # 1/2

                if not beat and red_reading > threshold_on:
                    beat = True
                    t_us = ticks_diff(ticks_us(), t_start)
                    t_s = t_us / 1000000
                    f = 1 / t_s
                    bpm = f * 60
                    if bpm < 500:
                        t_start = ticks_us()
                        beats_history.append(bpm)
                        beats_history = beats_history[-MAX_HISTORY:]  # 只保留最大30个元素数据
                        BEATS = round(sum(beats_history) / len(beats_history), 2)  # 四舍五入
                if beat and red_reading < threshold_off:
                    beat = False
            # ------------ 修改代码 -----------
            elif len(SPO2_LIST) < 10:
                # 计算血氧
                red_list.append(red_reading)
                ir_list.append(ir_reading)
                # 最多 只保留最新的100个
                red_list = red_list[-100:]
                ir_list = ir_list[-100:]
                # 计算血氧值
                if len(red_list) == 100 and len(ir_list) == 100:
                    hr, hrb, sp, spb = calc_hr_and_spo2(red_list, ir_list)
                    if hrb is True and spb is True:
                        if sp != -999:
                            SPO2 = int(sp)
            # ------------ 修改代码 -----------
            elif len(TEMP_LIST) < 10:
                # 计算温度
                TEMPERATURE = sensor.read_temperature()


if __name__ == '__main__':
    makerobo_setup(27)
    time_onandoff()
    create_oled0()
    button_setup()
    clock = Clock()
    i2c = I2C(scl=Pin(18), sda=Pin(19))
    accel = mpu6050.accel(i2c)

    server = MQTTClient("sbJY", "124.222.202.175")  # 建立一个MQTT客户端
    server.set_callback(sub_cb)  # 设置回调函数
    server.connect()  # 建立连接
    server.subscribe(b"MQTT")
    # server.subscribe(b"Time")

    # 监控ledctl这个通道，接收控制命令
    button_setup()
    # 1. 创建定时器
    timer = Timer(1)
    # 2. 设置定时器的回调函数，每1秒钟调用1次display_info函数（用来显示数据）
    timer.init(period=1000, mode=Timer.PERIODIC, callback=display_info)

    # ------------ 添加新代码 -----------
    # 3. 创建OLED屏幕

    # 4. 用来检测数据
    dataGetting()

